package utils

import (
	"context"
	"encoding/json"
	"testing"
	"time"

	"rdiot-demo/internal/eav/domain"
	"rdiot-demo/internal/eav/eav_service"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest"
)

// NewTestLogger 创建测试用的日志器
func NewTestLogger() *zap.Logger {
	return zaptest.NewLogger(&testing.T{})
}

// AssertTemplateEqual 比较两个模板是否相等
func AssertTemplateEqual(t *testing.T, expected, actual *domain.EntityTemplate) {
	assert.Equal(t, expected.ID, actual.ID)
	assert.Equal(t, expected.Name, actual.Name)
	assert.Equal(t, expected.DisplayName, actual.DisplayName)
	assert.Equal(t, expected.Description, actual.Description)
	assert.Equal(t, expected.IsActive, actual.IsActive)
}

// AssertAttributeTemplateEqual 比较两个属性模板是否相等
func AssertAttributeTemplateEqual(t *testing.T, expected, actual *domain.AttributeTemplate) {
	assert.Equal(t, expected.ID, actual.ID)
	assert.Equal(t, expected.Name, actual.Name)
	assert.Equal(t, expected.DisplayName, actual.DisplayName)
	assert.Equal(t, expected.DataType, actual.DataType)
	assert.Equal(t, expected.Required, actual.Required)
	assert.Equal(t, expected.EntityTemplateID, actual.EntityTemplateID)
}

// AssertEntityEqual 比较两个实体是否相等
func AssertEntityEqual(t *testing.T, expected, actual *domain.Entity) {
	assert.Equal(t, expected.ID, actual.ID)
	assert.Equal(t, expected.Name, actual.Name)
	assert.Equal(t, expected.TemplateID, actual.TemplateID)
	assert.Equal(t, expected.Status, actual.Status)
}

// AssertTimeEqual 断言时间相等（允许1秒误差）
func AssertTimeEqual(t *testing.T, expected, actual time.Time, message string) {
	diff := expected.Sub(actual)
	if diff < 0 {
		diff = -diff
	}
	assert.True(t, diff <= time.Second, "%s: 时间差超过1秒，期望: %v, 实际: %v", message, expected, actual)
}

// AssertValueEqual 断言值相等（根据数据类型）
func AssertValueEqual(t *testing.T, expected, actual interface{}, dataType string) {
	switch dataType {
	case "json":
		// JSON 类型需要序列化后比较
		expectedJSON, _ := json.Marshal(expected)
		actualJSON, _ := json.Marshal(actual)
		assert.JSONEq(t, string(expectedJSON), string(actualJSON), "JSON值不匹配")
	default:
		assert.Equal(t, expected, actual, "值不匹配")
	}
}

// GenerateTestUUID 生成测试用UUID
func GenerateTestUUID() uuid.UUID {
	return uuid.New()
}

// IsValidUUID 检查是否为有效UUID
func IsValidUUID(u uuid.UUID) bool {
	return u != uuid.Nil
}

// ContainsTemplate 检查模板列表中是否包含指定模板
func ContainsTemplate(templates []*domain.EntityTemplate, targetID uuid.UUID) bool {
	for _, template := range templates {
		if template.ID == targetID {
			return true
		}
	}
	return false
}

// ContainsEntity 检查实体列表中是否包含指定实体
func ContainsEntity(entities []*domain.Entity, targetID uuid.UUID) bool {
	for _, entity := range entities {
		if entity.ID == targetID {
			return true
		}
	}
	return false
}

// FilterEntitiesByStatus 按状态过滤实体
func FilterEntitiesByStatus(entities []*domain.Entity, status domain.EntityStatus) []*domain.Entity {
	var filtered []*domain.Entity
	for _, entity := range entities {
		if entity.Status == status {
			filtered = append(filtered, entity)
		}
	}
	return filtered
}

// DeepCopyTemplate 深度复制模板
func DeepCopyTemplate(original *domain.EntityTemplate) *domain.EntityTemplate {
	if original == nil {
		return nil
	}

	copy := *original

	// 复制属性定义
	if original.Attributes != nil {
		copy.Attributes = make([]domain.AttributeTemplate, len(original.Attributes))
		for i, attr := range original.Attributes {
			copy.Attributes[i] = attr
		}
	}

	return &copy
}

// DeepCopyEntity 深度复制实体
func DeepCopyEntity(original *domain.Entity) *domain.Entity {
	if original == nil {
		return nil
	}

	copy := *original

	// 复制属性值
	if original.Attributes != nil {
		copy.Attributes = make(map[string]interface{})
		for key, value := range original.Attributes {
			copy.Attributes[key] = value
		}
	}

	return &copy
}

// CreateTestTemplate 创建简单测试模板
func CreateTestTemplate(name, displayName, description string) *domain.EntityTemplate {
	return &domain.EntityTemplate{
		ID:          GenerateTestUUID(),
		Name:        name,
		DisplayName: displayName,
		Description: description,
		IsActive:    true,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
		Attributes:  []domain.AttributeTemplate{},
	}
}

// CreateTestEntity 创建简单测试实体
func CreateTestEntity(templateID uuid.UUID, name string) *domain.Entity {
	return &domain.Entity{
		ID:         GenerateTestUUID(),
		TemplateID: templateID,
		Name:       name,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
		CreatedBy:  "test_user",
		Status:     domain.EntityStatusActive,
		Attributes: map[string]interface{}{},
	}
}

// MockTemplateRepository Mock模板仓储
type MockTemplateRepository struct {
	mock.Mock
	templates map[uuid.UUID]*domain.EntityTemplate
	callLog   map[string]int
}

// NewMockTemplateRepository 创建Mock模板仓储
func NewMockTemplateRepository() *MockTemplateRepository {
	return &MockTemplateRepository{
		templates: make(map[uuid.UUID]*domain.EntityTemplate),
		callLog:   make(map[string]int),
	}
}

func (m *MockTemplateRepository) Create(ctx context.Context, template *domain.EntityTemplate) error {
	m.callLog["Create"]++
	m.templates[template.ID] = template
	return nil
}

func (m *MockTemplateRepository) GetByID(ctx context.Context, id uuid.UUID) (*domain.EntityTemplate, error) {
	m.callLog["GetByID"]++
	if template, exists := m.templates[id]; exists {
		return template, nil
	}
	return nil, nil
}

func (m *MockTemplateRepository) Update(ctx context.Context, template *domain.EntityTemplate) error {
	m.callLog["Update"]++
	m.templates[template.ID] = template
	return nil
}

func (m *MockTemplateRepository) Delete(ctx context.Context, id uuid.UUID) error {
	m.callLog["Delete"]++
	delete(m.templates, id)
	return nil
}

func (m *MockTemplateRepository) List(ctx context.Context, filter eav_service.TemplateFilter) ([]domain.EntityTemplate, int64, error) {
	m.callLog["List"]++
	var templates []domain.EntityTemplate
	for _, template := range m.templates {
		templates = append(templates, *template)
	}
	return templates, int64(len(templates)), nil
}

func (m *MockTemplateRepository) GetCallCount(method string) int {
	return m.callLog[method]
}

func (m *MockTemplateRepository) ClearCallLog() {
	m.callLog = make(map[string]int)
}

// MockEntityRepository Mock实体仓储
type MockEntityRepository struct {
	mock.Mock
	entities map[uuid.UUID]*domain.Entity
	callLog  map[string]int
}

// NewMockEntityRepository 创建Mock实体仓储
func NewMockEntityRepository() *MockEntityRepository {
	return &MockEntityRepository{
		entities: make(map[uuid.UUID]*domain.Entity),
		callLog:  make(map[string]int),
	}
}

func (m *MockEntityRepository) Create(ctx context.Context, entity *domain.Entity, attributes map[string]interface{}) error {
	m.callLog["Create"]++
	m.entities[entity.ID] = entity
	return nil
}

func (m *MockEntityRepository) GetByID(ctx context.Context, id uuid.UUID) (*domain.Entity, error) {
	m.callLog["GetByID"]++
	if entity, exists := m.entities[id]; exists {
		return entity, nil
	}
	return nil, nil
}

func (m *MockEntityRepository) Update(ctx context.Context, entity *domain.Entity, attributes map[string]interface{}) error {
	m.callLog["Update"]++
	m.entities[entity.ID] = entity
	return nil
}

func (m *MockEntityRepository) Delete(ctx context.Context, id uuid.UUID) error {
	m.callLog["Delete"]++
	delete(m.entities, id)
	return nil
}

func (m *MockEntityRepository) GetByTemplateID(ctx context.Context, templateID uuid.UUID, page, size int) ([]domain.Entity, int64, error) {
	m.callLog["GetByTemplateID"]++
	var entities []domain.Entity
	for _, entity := range m.entities {
		if entity.TemplateID == templateID {
			entities = append(entities, *entity)
		}
	}
	return entities, int64(len(entities)), nil
}

func (m *MockEntityRepository) Query(ctx context.Context, req eav_service.QueryEntitiesRequest) ([]domain.Entity, int64, error) {
	m.callLog["Query"]++
	var entities []domain.Entity
	for _, entity := range m.entities {
		// 简单的过滤逻辑
		if req.TemplateID != nil && entity.TemplateID != *req.TemplateID {
			continue
		}
		entities = append(entities, *entity)
	}
	return entities, int64(len(entities)), nil
}

func (m *MockEntityRepository) GetCallCount(method string) int {
	return m.callLog[method]
}

func (m *MockEntityRepository) ClearCallLog() {
	m.callLog = make(map[string]int)
}
